﻿Imports System.Collections.ObjectModel

Public Class AutoCompleteTextBox
#Region "Members"
    Private controls As VisualCollection
    Private textBox As TextBox
    Private comboBox As ComboBox
    Public autoCompletionList As ObservableCollection(Of AutoCompleteEntry)
    Private keypressTimer As System.Timers.Timer
    Private Delegate Sub TextChangedCallback()
    Private insertText As Boolean
    Private m_delayTime As Integer
    Private searchThreshold As Integer
#End Region

#Region "Constructor"
    Public Sub New()
        controls = New VisualCollection(Me)
        InitializeComponent()

        autoCompletionList = New ObservableCollection(Of AutoCompleteEntry)()
        searchThreshold = 2
        ' default threshold to 2 char
        ' set up the key press timer
        keypressTimer = New System.Timers.Timer()
        AddHandler keypressTimer.Elapsed, New System.Timers.ElapsedEventHandler(AddressOf OnTimedEvent)

        ' set up the text box and the combo box
        comboBox = New ComboBox()
        comboBox.IsSynchronizedWithCurrentItem = True
        comboBox.IsTabStop = False
        AddHandler comboBox.SelectionChanged, New SelectionChangedEventHandler(AddressOf comboBox_SelectionChanged)

        textBox = New TextBox()
        AddHandler textBox.TextChanged, New TextChangedEventHandler(AddressOf textBox_TextChanged)
        textBox.VerticalContentAlignment = VerticalAlignment.Center

        controls.Add(comboBox)
        controls.Add(textBox)
    End Sub
#End Region

#Region "Methods"
    Public Property Text() As String
        Get
            Return textBox.Text
        End Get
        Set(value As String)
            insertText = True
            textBox.Text = value
        End Set
    End Property

    Public Property DelayTime() As Integer
        Get
            Return m_delayTime
        End Get
        Set(value As Integer)
            m_delayTime = value
        End Set
    End Property

    Public Property Threshold() As Integer
        Get
            Return searchThreshold
        End Get
        Set(value As Integer)
            searchThreshold = value
        End Set
    End Property

    Public Sub AddItem(entry As AutoCompleteEntry)
        autoCompletionList.Add(entry)
    End Sub

    Private Sub comboBox_SelectionChanged(sender As Object, e As SelectionChangedEventArgs)
        If comboBox.SelectedItem IsNot Nothing Then
            insertText = True
            Dim cbItem As ComboBoxItem = DirectCast(comboBox.SelectedItem, ComboBoxItem)
            textBox.Text = cbItem.Content.ToString()
            RaiseEvent ValueChanged(cbItem.DataContext)
        End If
    End Sub

    Private Sub TextChanged()
        Try
            comboBox.Items.Clear()
            If textBox.Text.Length >= searchThreshold Then
                For Each entry As AutoCompleteEntry In autoCompletionList
                    For Each word As String In entry.KeywordStrings
                        If word.StartsWith(textBox.Text, StringComparison.CurrentCultureIgnoreCase) Then
                            Dim cbItem As New ComboBoxItem()
                            cbItem.Content = entry.ToString()
                            cbItem.DataContext = entry
                            comboBox.Items.Add(cbItem)
                            Exit For
                        End If
                    Next
                Next
                comboBox.IsDropDownOpen = comboBox.HasItems
            Else
                comboBox.IsDropDownOpen = False
            End If
        Catch
        End Try
    End Sub

    Private Sub OnTimedEvent(source As Object, e As System.Timers.ElapsedEventArgs)
        keypressTimer.[Stop]()
        Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, New TextChangedCallback(AddressOf Me.TextChanged))
    End Sub

    Public Event ValueChanged(sender As Object)

    Private Sub textBox_TextChanged(sender As Object, e As TextChangedEventArgs)
        ' text was not typed, do nothing and consume the flag
        If insertText = True Then
            insertText = False
        Else

            ' if the delay time is set, delay handling of text changed
            If m_delayTime > 0 Then
                keypressTimer.Interval = m_delayTime
                keypressTimer.Start()
            Else
                TextChanged()
            End If
        End If
    End Sub

    Protected Overrides Function ArrangeOverride(arrangeSize As Size) As Size
        textBox.Arrange(New Rect(arrangeSize))
        comboBox.Arrange(New Rect(arrangeSize))
        Return MyBase.ArrangeOverride(arrangeSize)
    End Function

    Protected Overrides Function GetVisualChild(index As Integer) As Visual
        Return controls(index)
    End Function

    Protected Overrides ReadOnly Property VisualChildrenCount() As Integer
        Get
            Return controls.Count
        End Get
    End Property
#End Region
End Class
